﻿//==============================================================================
// File: ManyToManyAssocSync.cs
// Created: 2010-05-10
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Collections;
using System.Collections.Specialized;

namespace WpfRcp.PropertyModel.Internal.Associations
{
    /// <summary>
    /// Keeps both sides of a bidirectional many-to-many association in sync with each other.
    /// </summary>
    internal static class ManyToManyAssocSync
    {
        public static void ResynchronizeOtherSide(PropertyContainer thisSide, MetaProperty thisSideProperty, MetaProperty otherSideProperty, object thisSideCollection)
        {
            if (thisSideCollection != null && !thisSide.IsSynchronizing(thisSideProperty))
            {
                using (thisSide.BlockSynchronizationReentrency(thisSideProperty))
                {
                    var thisSideList = (IList)thisSideCollection;
                    foreach (PropertyContainer addingToOtherSide in thisSideList)
                    {
                        if (!addingToOtherSide.IsSynchronizing(otherSideProperty))
                        {
                            var otherSideValue = addingToOtherSide.GetValue(otherSideProperty);
                            
                            // Other side may be null if collections are during first initialization
                            if (otherSideValue == null)
                                return;

                            var otherSidesCollection = otherSideValue as IList;
                            if (otherSidesCollection == null)
                                throw new ArgumentException(
                                    "To use ManyToMany sychronization other property must be of type that implements IList");

                            if (!otherSidesCollection.Contains(thisSide))
                            {
                                otherSidesCollection.Add(thisSide);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Responds to add/remove events raised by this side's collection.
        /// </summary>
        public static void UpdateOtherSide(PropertyContainer thisSide, MetaProperty thisSideProperty, MetaProperty otherSideProperty, NotifyCollectionChangedEventArgs e)
        {
            if (!thisSide.IsSynchronizing(thisSideProperty))
            {
                using (thisSide.BlockSynchronizationReentrency(thisSideProperty))
                {
                    if (e.Action == NotifyCollectionChangedAction.Add)
                    {
                        foreach (PropertyContainer addingToOtherSide in e.NewItems)
                        {
                            if (!addingToOtherSide.IsSynchronizing(otherSideProperty))
                            {
                                var otherSideValue = addingToOtherSide.GetValue(otherSideProperty);
                                
                                // Other side may be null if collections are during first initialization
                                if (otherSideValue == null)
                                    return;

                                var otherSidesCollection = otherSideValue as IList;
                                if (otherSidesCollection == null)
                                    throw new ArgumentException(
                                        "To use ManyToMany sychronization other property must be of type that implements IList");

                                if (!otherSidesCollection.Contains(thisSide))
                                {
                                    otherSidesCollection.Add(thisSide);
                                }
                            }
                        }
                    }
                    else if (e.Action == NotifyCollectionChangedAction.Remove)
                    {
                        foreach (PropertyContainer removingFromOtherSide in e.OldItems)
                        {
                            if (!removingFromOtherSide.IsSynchronizing(otherSideProperty))
                            {

                                var otherSidesCollection = removingFromOtherSide.GetValue(otherSideProperty) as IList;
                                if (otherSidesCollection == null)
                                    throw new ArgumentException(
                                        "To use ManyToMany sychronization other property must be of type that implements IList");

                                if (otherSidesCollection.Contains(thisSide))
                                {
                                    otherSidesCollection.Remove(thisSide);
                                }
                            }
                        }
                    }
                    else
                    {
                        throw new NotSupportedException("Handling such collection change is not supported.");
                    }
                }
            }
        }
    }
}